;;;; company-glsl.el -*- lexical-binding: t; -*-

;;
;; There is another one:
;; https://github.com/Kaali/company-glsl
;;

;;
;; TODO: cl-sort in defcustom is still not the ideal solution
;;       think up what to do with it
;;

(require 'glsl-mode) ;; https://github.com/jimhourihan/glsl-mode

(defgroup company-glsl nil
  "A simple company backend for GLSL - GL Shader Language, v4.6."
  :group 'company)

(defcustom company-glsl-use-depricated nil
  "Define whether to use completion candidates depricated by the standard."
  :group 'company-glsl
  :type 'boolean
  :risky t)

(defcustom company-glsl-use-preprocessor-expressions nil
  "Define whether to use preprocessor expressions as candidated for completion."
  :group 'company-glsl
  :type 'boolean
  :risky t)

(defcustom company-glsl-preprocessor-directive-candidates
  (cl-sort (mapcar (lambda (str) (concat "#" str))
		   glsl-preprocessor-directive-list)
	   #'string-lessp)
  "String list of preprocessor directive candidates for completion. Sorted by string<."
  :group 'company-glsl
  :type 'list
  :risky t)

(defcustom company-glsl-preprocessor-builtin-candidates
  (cl-sort glsl-preprocessor-builtin-list #'string-lessp)
  "String list of preprocessor directive builtins for completion. Sorted by string<."
  :group 'company-glsl
  :type 'list
  :risky t)

(defcustom company-glsl-normal-candidates
  (cl-sort (append glsl-builtin-list
		   glsl-reserved-list
		   glsl-keyword-list
		   glsl-qualifier-list
		   glsl-type-list
		   (when company-glsl-use-depricated
		     (append glsl-deprecated-builtin-list
			     glsl-deprecated-variables-list
			     glsl-deprecated-qualifier-list))
		   (when company-glsl-use-preprocessor-expressions
		     glsl-preprocessor-expr-list))
	   #'string-lessp)
  "String list of normal candidates for completion. Sorted by string<."
  :group 'company-glsl
  :type 'list
  :risky t)

(defun company-glsl--process-candidates (candidates word-before-point)
  "Clean up redudant CANDIDATES and sort them by similarity to WORD-BEFORE-POINT."
  (cl-sort
   (seq-filter (lambda (candidate)
		 (string-match-p word-before-point candidate))
	       candidates)
   (lambda (s1 s2) (< (string-distance s1 word-before-point)
		      (string-distance s2 word-before-point)))))

;; stolen from company-elixir
(defun company-glsl--get-word-before-point nil
  "Get word before point, including special character. A crude thing."
  (if (or (looking-at "[\s();]=-+*/") (eolp))
      (let (p1 p2 (skip-chars "-_A-Za-z0-9.?!@:#"))
	(save-excursion
	  (skip-chars-backward skip-chars)
	  (setq p1 (point))
	  (skip-chars-forward skip-chars)
	  (setq p2 (point))
	  (buffer-substring-no-properties p1 p2)))))

(defun company-glsl-backend
      (command &optional arg &rest ignored)
    "Simplistic company backend based on jimhourihan's glsl-mode's stored variables. For OpenGL v4.6."
    (interactive (list 'interactive))
    (cl-case command
      (interactive (company-begin-backend 'company-glsl-backend-custom))
      (prefix (and (eq major-mode 'glsl-mode)
		   (company-glsl--get-word-before-point)))
      (candidates
       (cond ((string-prefix-p "#" arg)
	      (company-glsl--process-candidates
	       company-glsl-preprocessor-directive-candidates arg))
	     ((string-prefix-p "__" arg)
	      (company-glsl--process-candidates
	       company-glsl-preprocessor-builtin-candidates arg))
	     (t (company-glsl--process-candidates
		 company-glsl-normal-candidates arg))))
      (sorted t)
      (duplicates t)))

(provide 'company-glsl)
